//
// Game.java
//

package net.kadigan.sciencefair2010.tictactoe;

import android.content.Context;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.widget.Toast;

public class Game {

  //
  // Constructors
  //

  public Game(Context context) {
    this.context = context;
  }

  //
  // Additional methods
  //

  public void output(String message) {
    Toast toast = Toast.makeText(this.context, message, Toast.LENGTH_LONG);
    toast.setGravity(Gravity.CENTER, 0, 0);
    toast.show();
  }

  public View initialize() {

    // Initialize the board.
    this.board = new Board(this.context);
    this.board.requestFocus();

    // Initialize the players.
    // TODO: Allow the user to select the player types. 
    this.playerX = new GuiHumanPlayer(Player.Name.X, this.board);
    this.playerO = new SmartComputerPlayer(Player.Name.O, this.board);
    this.currentPlayer = this.playerX;

    // Initialize the game variables.
    this.numberOfMoves = 0;
    this.gameOver = false;

    // Start getting the moves.
    this.handler = new Handler();
    this.handler.postDelayed(this.updateTimeTask, this.pause);

    return this.board;

  }

  public void cleanup() {
    if (this.handler != null) {
      this.handler.removeCallbacks(this.updateTimeTask);
      this.handler = null;
    }
  }

  private void processMove(Location move) {

    // Store the move if it is valid.
    if (!this.gameOver && this.board.setMove(move, this.currentPlayer)) {

      this.board.invalidate();
      this.numberOfMoves++;

      // Check for the end of the game.
      if (this.checkForWin()) {
        this.output("Player " + this.currentPlayer.getName() + " wins!");
        this.gameOver = true;
      }
      if (this.numberOfMoves == 9) {
        this.output("Draw");
        this.gameOver = true;
      }

      // Switch players.
      if (this.currentPlayer == this.playerX) {
        this.currentPlayer = this.playerO;
      }
      else {
        this.currentPlayer = this.playerX;
      }

    }

  }

  private boolean checkForWin() {
    boolean won;
    won = this.checkForWinInRows();
    if (won) {
      return true;
    }
    won = this.checkForWinInColumns();
    if (won) {
      return true;
    }
    won = this.checkForWinInDiagonal1();
    if (won) {
      return true;
    }
    won = this.checkForWinInDiagonal2();
    return won;
  }

  private boolean checkForWinInRows() {
    boolean won;
    for (int row = 0; row < Board.MAX_ROWS; row++) {
      won = this.checkForWinInRow(row);
      if (won) {
        return true;
      }
    }
    return false;
  }

  private boolean checkForWinInRow(int row) {
    Player.Name m0 = this.board.getMove(row, 0);
    if (m0 == Player.Name.NONE) {
      return false;
    }
    for (int column = 0; column < Board.MAX_COLUMNS; column++) {
      if (this.board.getMove(row, column) != m0) {
        return false;
      }
    }
    return true;
  }

  private boolean checkForWinInColumns() {
    boolean won;
    for (int column = 0; column < Board.MAX_COLUMNS; column++) {
      won = this.checkForWinInColumn(column);
      if (won) {
        return true;
      }
    }
    return false;
  }

  private boolean checkForWinInColumn(int column) {
    Player.Name m0 = this.board.getMove(0, column);
    if (m0 == Player.Name.NONE) {
      return false;
    }
    for (int row = 0; row < Board.MAX_ROWS; row++) {
      if (this.board.getMove(row, column) != m0) {
        return false;
      }
    }
    return true;
  }

  private boolean checkForWinInDiagonal1() {
    Player.Name m0 = this.board.getMove(0, 0);
    if (m0 == Player.Name.NONE) {
      return false;
    }
    for (int row = 0; row < Board.MAX_ROWS; row++) {
      if (this.board.getMove(row, row) != m0) {
        return false;
      }
    }
    return true;
  }

  private boolean checkForWinInDiagonal2() {
    Player.Name m0 = this.board.getMove(0, 2);
    if (m0 == Player.Name.NONE) {
      return false;
    }
    for (int row = 0; row < Board.MAX_ROWS; row++) {
      if (this.board.getMove(row, 2-row) != m0) {
        return false;
      }
    }
    return true;
  }

  //
  // Attributes
  //

  private Context context;

  private Board board;

  private Player playerX;

  private Player playerO;

  private Player currentPlayer;

  private int numberOfMoves;

  private boolean gameOver;

  private Handler handler;

  private final int pause = 500;

  private Runnable updateTimeTask = new Runnable() {
    //@Override
    public void run() {
      Game.this.processMove(Game.this.currentPlayer.getMove());
      if (Game.this.gameOver) {
        Game.this.cleanup();
      }
      else {
        Game.this.handler.postDelayed(this, Game.this.pause);
      }
    }
  };

}
